﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Configuration;
using System.Xml;

namespace PizzaLunch
{
    public static class MainControl
    {
        private static List<TweetSource> AllSource = new List<TweetSource>();
        public static TweetList ItemList = new TweetList();
        public static TweetAccounts AllAcount = new TweetAccounts();
        public static List<UserInfo> AllUserList = new List<UserInfo>();
        public static CurrentUser me = new CurrentUser(null);
        public static LoggerFile Logger;


        public delegate void UpdateTweetsDel(TweetItem ti);
        public static UpdateTweetsDel UpdateTweets;
        public delegate void UpdateCommentsDel(TweetItem ti, TweetItem tiComment);
        public static UpdateCommentsDel UpdateComments;
        public delegate void UpdateFansDel(UserInfo usInfo, UserInfo fans, UserInfo.UserType type);
        public static UpdateFansDel UpdateFans;
        public delegate void UpdateUserTimelineDel(UserInfo usInfo, TweetItem ti);
        public static UpdateUserTimelineDel UpdateUserTimeline;
        public delegate void UpdateAllUserDel(UserInfo usInfo, TweetItem ti);
        public static UpdateAllUserDel UpdateAllUser;

        public static bool Stopped { get; set; }
        private static bool _paused = false;
        public static bool Paused
        {
            get { return _paused; }
            set
            {
                _paused = value;
                if (value)
                    pausedEvent.Reset();
                else
                    pausedEvent.Set();
            }
        }

        private static JobList _jobStack = new JobList();
        public static JobList JobStack
        {
            get { return _jobStack; }
            /*get
            {
                return JobList.Synchronized(_jobStack);
            }*/
        }

        public static void Compose(string Content)
        {
            foreach (TweetAccount tac in AllAcount)
            {
                if (!tac.Valid||!tac.AccountCfg.allowCompose)
                    continue;
                tac.Source.Compose(tac,Content);
            }
        }

        public static void AddQueryTimelineJob(TweetAccount tac)
        {
            Logger.WriteLog("Add query timeline job start", LoggerFile.LoggerLevel.LoggerProcessing);
            QueryTimelineJob jobnew = new QueryTimelineJob();
            jobnew.Account = tac;

            JobStack.Push(jobnew);
            Logger.WriteLog("Add query timeline job push event", LoggerFile.LoggerLevel.LoggerDebug);
            Logger.WriteLog("Add query job exit", LoggerFile.LoggerLevel.LoggerProcessing);
        }

        public static void AddCommentsJob(TweetItem ti)
        {
            Logger.WriteLog("Add comments job enter", LoggerFile.LoggerLevel.LoggerProcessing);
            QueryCommentsJob jobnew = new QueryCommentsJob();
            jobnew.Item = ti;

            JobStack.Push(jobnew);
            Logger.WriteLog("Add comments job push event", LoggerFile.LoggerLevel.LoggerDebug);
            Logger.WriteLog("Add comments job exit", LoggerFile.LoggerLevel.LoggerProcessing);
        }

        public static void AddQueryFansJob(TweetAccount tac,UserInfo user,int index)
        {
            Logger.WriteLog("Add query fans job enter", LoggerFile.LoggerLevel.LoggerProcessing);

            QueryFansJob jobnew = new QueryFansJob();
            jobnew.Account = tac;
            jobnew.User = user;
            jobnew.Index = index;

            JobStack.Push(jobnew);
            Logger.WriteLog("Add query fans job push event", LoggerFile.LoggerLevel.LoggerDebug);
            Logger.WriteLog("Add query fans job exit", LoggerFile.LoggerLevel.LoggerProcessing);
        }

        public static void AddQueryFriendsJob(TweetAccount tac, UserInfo user,int index)
        {
            Logger.WriteLog("Add query friends job enter", LoggerFile.LoggerLevel.LoggerProcessing);
            QueryFriendsJob jobnew = new QueryFriendsJob();
            jobnew.Account = tac;
            jobnew.User = user;
            jobnew.Index = index;

            JobStack.Push(jobnew);
            Logger.WriteLog("Add query friends job push event", LoggerFile.LoggerLevel.LoggerDebug);
            Logger.WriteLog("Add query friends job exit", LoggerFile.LoggerLevel.LoggerProcessing);
        }

        public static void AddQueryUserTimelineJob(TweetAccount tac,UserInfo user)
        {
            Logger.WriteLog("Add query user timeline job start", LoggerFile.LoggerLevel.LoggerProcessing);
            QueryUserTimelineJob jobnew = new QueryUserTimelineJob();
            jobnew.Account = tac;
            jobnew.User = user;

            JobStack.Push(jobnew);
            Logger.WriteLog("Add query user timeline job push event", LoggerFile.LoggerLevel.LoggerDebug);
            Logger.WriteLog("Add query user timeline job exit", LoggerFile.LoggerLevel.LoggerProcessing);
        }

        private static DataRecvThread DataThread = new DataRecvThread();
        private static Thread th;

        public static AutoResetEvent queryEvent = new AutoResetEvent(false);
        public static ManualResetEvent pausedEvent = new ManualResetEvent(false);

        private static Timer queryTimer;
        private static Util.GlobalSetting gSetting = new Util.GlobalSetting();
        public static Util.GlobalSetting GlobalSetting
        {
            get { return gSetting; }
        }

        private static void TimerCallback(object state)
        {
            if (MainControl.Paused)
                return;
            Logger.WriteLog("Query Timer call", LoggerFile.LoggerLevel.LoggerProcessing);
            foreach (TweetAccount tac in MainControl.AllAcount)
            {
                MainControl.AddQueryTimelineJob(tac);
            }
        }

        public static TweetAccount GetDefaultAccount()
        {
            TweetAccount defAcc = MainControl.AllAcount.SingleOrDefault((tac) =>
            {
                if (tac.AccountCfg.isDefault == true)
                    return true;
                return false;
            });
            return defAcc;
        }

        public static TweetSource GetSource(string ID)
        {
            TweetSource ts = MainControl.AllSource.Find((s) => { if (s.ID == ID) return true; return false; });
            return ts;
        }
        public static void Init()
        {
            GlobalSetting.Load();
            Logger = new LoggerFile(GlobalSetting.LOGGER_FILE_NAME,GlobalSetting.LOGGER_FILE_LEVEL);

            AllSource.Add(new SinaSource());
            AllSource.Add(new TXSource());

            Util.AccountConfig.Load(AllAcount, AddAccount);
            foreach (TweetAccount tac in AllAcount)
            {
                tac.GetUserInfo();
            }

            if (GetDefaultAccount() == null && AllAcount.Count>0)
            {
                AllAcount[0].AccountCfg.isDefault = true;
            }

            Stopped = false;
            Paused = false;
            th = new Thread(DataThread.ThreadMain);
        }

        public static void Start()
        {
            Logger.WriteLog("MainControl Start Enter", LoggerFile.LoggerLevel.LoggerProcessing);
            Stopped = false;
            th.Start();
            queryTimer = new Timer(TimerCallback, null, 1000, gSetting.REFRESH_TIME_PEROID);
            Logger.WriteLog("MainControl Start Exit", LoggerFile.LoggerLevel.LoggerProcessing);
        }

        public static void Clear()
        {
            Stopped = true;
            queryEvent.Set();
            Util.AccountConfig.Save(AllAcount);
            gSetting.Save();
        }

        private static void AddAccount(Util.AccountConfig acg)
        {
            TweetSource ts = MainControl.GetSource(acg.sourceID);
            if (ts != null)
            {
                TweetAccount tac = new TweetAccount(ts);
                tac.AccountCfg = acg;
                AllAcount.Add(tac);
            }
        }

        public static void RefreshQuery()
        {
            Logger.WriteLog("MainControl Refresh Query", LoggerFile.LoggerLevel.LoggerProcessing);
            queryTimer.Change(0, gSetting.REFRESH_TIME_PEROID);
        }
    }

    public class DataRecvThread
    {
        public void ThreadMain()
        {
            //MainControl.queryEvent.WaitOne();
            MainControl.Logger.WriteLog("Query thread Start", LoggerFile.LoggerLevel.LoggerProcessing);
            try
            {
                while (!MainControl.Stopped)
                {
                    while (MainControl.JobStack.Count > 0)
                    {
                        if (MainControl.Stopped)
                            return;
                        if (MainControl.Paused)
                            MainControl.pausedEvent.WaitOne();
                        MainControl.Logger.WriteLog("PausedEvent Got", LoggerFile.LoggerLevel.LoggerDebug);
                        QueryJob job = MainControl.JobStack.Pop();
                        if (job == null)
                            continue;
                        MainControl.Logger.WriteLog("Query got a job", LoggerFile.LoggerLevel.LoggerDebug);
                        try
                        {
                            job.Working();
                        }
                        catch (Exception e)
                        {
                            MainControl.Logger.WriteLog("Job working error " + e.ToString(), LoggerFile.LoggerLevel.LoggerError);
                        }
                    }
                    MainControl.queryEvent.WaitOne();
                    MainControl.Logger.WriteLog("QueryEvent Got", LoggerFile.LoggerLevel.LoggerDebug);
                }
            }
            catch (Exception e)
            {
                MainControl.Logger.WriteLog("Query thread error with " + e.ToString(), LoggerFile.LoggerLevel.LoggerError);
            }
        }
    }

    public static class Util
    {
        public class GlobalSetting
        {
            public int REFRESH_TIME_PEROID { get; set; }
            public int REFRESH_ITEM_COUNT { get; set; }
            public string LOGGER_FILE_NAME { get; set; }
            public LoggerFile.LoggerLevel LOGGER_FILE_LEVEL { get; set; }

            public void Load()
            {
                if (ConfigurationManager.AppSettings["refresh_timer"]!=null)
                    REFRESH_TIME_PEROID = Convert.ToInt32(ConfigurationManager.AppSettings["refresh_timer"]);
                else
                    REFRESH_TIME_PEROID = 60000;

                if (ConfigurationManager.AppSettings["refresh_count"]!=null)
                    REFRESH_ITEM_COUNT = Convert.ToInt32(ConfigurationManager.AppSettings["refresh_count"]);
                else
                    REFRESH_ITEM_COUNT = 20;

                if (ConfigurationManager.AppSettings["log_file"]!=null)
                    LOGGER_FILE_NAME = ConfigurationManager.AppSettings["log_file"];
                else
                    LOGGER_FILE_NAME = null;
                if (ConfigurationManager.AppSettings["log_level"] != null)
                {
                    switch (ConfigurationManager.AppSettings["log_level"].ToLower())
                    {
                        case "error":
                            LOGGER_FILE_LEVEL = LoggerFile.LoggerLevel.LoggerError;
                            break;
                        case "warning":
                            LOGGER_FILE_LEVEL = LoggerFile.LoggerLevel.LoggerWaring;
                            break;
                        case "processing":
                            LOGGER_FILE_LEVEL = LoggerFile.LoggerLevel.LoggerProcessing;
                            break;
                        case "information":
                            LOGGER_FILE_LEVEL = LoggerFile.LoggerLevel.LoggerInformation;
                            break;
                        case "detail":
                            LOGGER_FILE_LEVEL = LoggerFile.LoggerLevel.LoggerDetail;
                            break;
                        case "debug":
                            LOGGER_FILE_LEVEL = LoggerFile.LoggerLevel.LoggerDebug;
                            break;
                        default:
                            LOGGER_FILE_LEVEL = LoggerFile.LoggerLevel.LoggerNone;
                            break;
                    }
                }
                else
                    LOGGER_FILE_LEVEL = LoggerFile.LoggerLevel.LoggerNone;
            }
            public void Save()
            {
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                if (config.AppSettings.Settings["refresh_timer"] == null)
                    config.AppSettings.Settings.Add("refresh_timer", REFRESH_TIME_PEROID.ToString());
                else
                    config.AppSettings.Settings["refresh_timer"].Value = REFRESH_TIME_PEROID.ToString();
                if (config.AppSettings.Settings["refresh_count"] == null)
                    config.AppSettings.Settings.Add("refresh_count", REFRESH_ITEM_COUNT.ToString());
                else
                    config.AppSettings.Settings["refresh_count"].Value = REFRESH_ITEM_COUNT.ToString();
                config.Save(ConfigurationSaveMode.Minimal);
            }
        }

        #region SourceConfig
        public class SourceConfig : ConfigurationSection
        {
            [ConfigurationProperty("custom_key")]
            public string customKey
            {
                get { return (string)this["custom_key"]; }
                set { this["custom_key"] = value; }
            }

            [ConfigurationProperty("custom_secrect")]
            public string customSecrect
            {
                get { return (string)this["custom_secrect"]; }
                set { this["custom_secrect"] = value; }
            }

            [ConfigurationProperty("base_url")]
            public string baseUrl
            {
                get { return (string)this["base_url"]; }
                set { this["base_url"] = value; }
            }

            [ConfigurationProperty("request_url")]
            public string requestUrl
            {
                get { return (string)this["request_url"]; }
                set { this["request_url"] = value; }
            }

            [ConfigurationProperty("callback_url")]
            public string callbackUrl
            {
                get { return (string)this["callback_url"]; }
                set { this["callback_url"] = value; }
            }

            public void CopyConfig(SourceConfig scg)
            {
                customKey = scg.customKey;
                customSecrect = scg.customSecrect;
                baseUrl = scg.baseUrl;
                requestUrl = scg.requestUrl;
                callbackUrl = scg.callbackUrl;
            }

            public void Save(string SourceName)
            {
                try
                {
                    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                    if (config.SectionGroups["SourceConfig"] == null)
                        config.SectionGroups.Add("SourceConfig", new ConfigurationSectionGroup());
                    SourceConfig cfg2 = config.SectionGroups["SourceConfig"].Sections.Get(SourceName) as SourceConfig;
                    if (cfg2 == null)
                    {
                        cfg2 = new SourceConfig();
                        cfg2.CopyConfig(this);
                        config.SectionGroups["SourceConfig"].Sections.Add(SourceName, cfg2);
                    }
                    else
                    {
                        cfg2.CopyConfig(this);
                    }
                    config.Save(ConfigurationSaveMode.Minimal);
                    ConfigurationManager.RefreshSection(SourceName);
                }
                catch
                {
                }
            }
            public bool Load(string SourceName)
            {
                try
                {
                    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                    if (config.SectionGroups["SourceConfig"] == null)
                        return false;
                    CopyConfig(config.SectionGroups["SourceConfig"].Sections.Get(SourceName) as SourceConfig);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
        }
        #endregion

        #region AccountConfig
        public class AccountConfig : ConfigurationSection
        {
            [ConfigurationProperty("source_id")]
            public string sourceID
            {
                get { return (string)this["source_id"]; }
                set { this["source_id"] = value; }
            }

            [ConfigurationProperty("access_key")]
            public string accessKey
            {
                get { return (string)this["access_key"]; }
                set { this["access_key"] = value; }
            }

            [ConfigurationProperty("access_secrect")]
            public string accessSecrect
            {
                get { return (string)this["access_secrect"]; }
                set { this["access_secrect"] = value; }
            }

            [ConfigurationProperty("allow_compose")]
            public bool allowCompose
            {
                get { return (bool)this["allow_compose"]; }
                set { this["allow_compose"] = value; }
            }

            [ConfigurationProperty("default")]
            public bool isDefault
            {
                get { return (bool)this["default"]; }
                set { this["default"] = value; }
            }

            public void CopyConfig(AccountConfig acg)
            {
                sourceID = acg.sourceID;
                accessKey = acg.accessKey;
                accessSecrect = acg.accessSecrect;
                allowCompose = acg.allowCompose;
                isDefault = acg.isDefault;
            }

            public delegate void AddAccount(AccountConfig acg);
            public static void Load(TweetAccounts accList, AddAccount add)
            {
                try
                {
                    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                    if (config.SectionGroups["AccountConfig"] == null)
                        return;
                    foreach (ConfigurationSection cfg in config.SectionGroups["AccountConfig"].Sections)
                    {
                        if (add != null)
                            add(cfg as AccountConfig);
                    }
                }
                catch
                {
                }
            }
            public static void Save(TweetAccounts accList)
            {
                try
                {
                    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                    if (config.SectionGroups["AccountConfig"] != null)
                        config.SectionGroups.Remove("AccountConfig");
                    config.SectionGroups.Add("AccountConfig", new ConfigurationSectionGroup());

                    foreach (TweetAccount tac in accList)
                    {
                        string ID = tac.Source.ID + "_" + tac.ID;
                        AccountConfig acg2 = new AccountConfig();
                        acg2.CopyConfig(tac.AccountCfg);
                        config.SectionGroups["AccountConfig"].Sections.Add(ID, acg2);
                    }
                    config.Save(ConfigurationSaveMode.Minimal);
                }
                catch
                {
                }
            }
        }
        #endregion

        #region DateTransfer
        public static DateTime ConvertFromUnixTimestamp(long timestamp)
        {
            DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
            return origin.AddSeconds(timestamp).ToLocalTime();
        }
        public static long ConvertToUnixTimestamp(DateTime date)
        {
            DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
            TimeSpan diff = date - origin;
            return (long)diff.TotalSeconds;
        }

        /// <summary>
        /// DateTransfer
        /// </summary>
        /// <param name="time"> Mon Oct 18 12:28:54 +0800 2010 </param>
        /// <returns>DateTime</returns>
        public static DateTime ConverDateTime(string time)
        {
            DateTime DT = DateTime.ParseExact(time, "ddd MMM dd HH:mm:ss zzz yyyy", System.Globalization.CultureInfo.InvariantCulture);
            return DT;
        }
        #endregion
    }
}
